/******************************************************************************
*  Copyright(c) 2021-2023 Sietium
*
*  @file:    skybox.cpp
*  @brif:    skybox and obj model
*
*  @date:    05/10/2021
*  @Author:  cary.Zhang
*
******************************************************************************/
#include <string>
#include <cstdlib>
#include <vector>
#include <array>
#include <memory>
#include "vmath.h"
#include "glad/gl-headers.h"

#include "model.h"
#include "glslprogram.h"
#include "texture.h"
#include "objectfactory.h"
#include "node.h"
#include "vertex_roll.h"

class Skybox: public Node
{
public:
    Skybox();
protected:
    bool startup();
    void render(double currentTime, double difTime);
    void shutdown();
protected:
    GLSLProgram programRender;
    GLSLProgram programSkyBox;

    GLuint texEnvmap;
    GLuint vaoSky, vaoBall;
    GLuint vboBall;
};

Skybox::Skybox()
{
    nodeName = "Skybox";
}

bool Skybox::startup()
{
    if (PerfWindow::glVersion < 330) {
        printf("Failed! The GL version is less than 3.3 !\n");
        return false;
    }

    if (!programRender.build("../media/shaders/skybox/render.vs.glsl",
                             "../media/shaders/skybox/render.fs.glsl"))
        return false;

    if (!programSkyBox.build("../media/shaders/skybox/skybox.vs.glsl",
                             "../media/shaders/skybox/skybox.fs.glsl"))
        return false;

#ifndef USE_GLES
    /* OpenGL ES 3.0 requires that all cube map filtering be seamless,
     * (See Appendix F.2 "Differences in Runtime Behavior" of the OpenGL ES 3.0 spec)
     * so no longer need to enable GL_TEXTURE_CUBE_MAP_SEAMLESS for GLES. */
    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif

    std::array<std::string, 6> pictures = {
        "../media/textures/SanFrancisco3/posx.jpg",
        "../media/textures/SanFrancisco3/negx.jpg",
        "../media/textures/SanFrancisco3/posy.jpg",
        "../media/textures/SanFrancisco3/negy.jpg",
        "../media/textures/SanFrancisco3/posz.jpg",
        "../media/textures/SanFrancisco3/negz.jpg"
    };

    if (!Texture::loadCubeMap(pictures, &texEnvmap))
        return false;

    glGenVertexArrays(1, &vaoSky);
    glBindVertexArray(vaoSky);
    glBindVertexArray(0);
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
    glCullFace(GL_BACK);

    glGenVertexArrays(1, &vaoBall);
    glBindVertexArray(vaoBall);

    glGenBuffers(1, &vboBall);
    glBindBuffer(GL_ARRAY_BUFFER, vboBall);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_roll) + sizeof(normals_roll), NULL, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_roll), vertex_roll);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertex_roll), sizeof(normals_roll), normals_roll);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(vertex_roll)));

    glBindVertexArray(0);

    return true;
}

void Skybox::render(double currentTime, double difTime)
{
    (void)difTime;
    static const GLfloat gray[] = { 0.2f, 0.2f, 0.2f, 1.0f };
    static const GLfloat ones[] = { 1.0f };
    const float tf = currentTime * 0.3f;

    vmath::mat4 projMatrix = vmath::perspective(60.0f, getWindowRatio(), 0.1f, 1000.0f);
    vmath::mat4 viewMatrix = vmath::lookat(vmath::vec3(15.0f * sinf(tf), 0.0f, 15.0f * cosf(tf)),
                                           vmath::vec3(0.0f, 0.0f, 0.0f),
                                           vmath::vec3(0.0f, 1.0f, 0.0f));
    vmath::mat4 zoomMatrix = vmath::scale(0.10f, 0.10f, 0.10f);
    vmath::mat4 mvMatrix = viewMatrix * zoomMatrix *
//                            vmath::rotate(tf, 1.0f, 0.0f, 0.0f) *
//                            vmath::rotate((float)difTime*0.1f, 0.0f, 1.0f, 0.0f) *
                           vmath::translate(0.0f, 0.0f, 0.0f);

    glClearBufferfv(GL_COLOR, 0, gray);
    glClearBufferfv(GL_DEPTH, 0, ones);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texEnvmap);

    glViewport(0, 0, getWidth(), getHeight());

    programSkyBox.use();

    programSkyBox.setUniformMatrix4fv("view_matrix", viewMatrix.operator float * ());

    glDisable(GL_DEPTH_TEST);

    glBindVertexArray(vaoSky);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    //glEnable(GL_DEPTH_TEST);
    programRender.use();

    programRender.setUniformMatrix4fv("mv_matrix", mvMatrix);
    programRender.setUniformMatrix4fv("proj_matrix", projMatrix);

    glBindVertexArray(vaoBall);
    glDrawArrays(GL_TRIANGLES, 0, numofroll);
}

void Skybox::shutdown()
{
    programRender.clear();
    programSkyBox.clear();

    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    glUseProgram(0);
    glBindVertexArray(0);
#ifndef USE_GLES
    glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    glDeleteVertexArrays(1, &vaoBall);
    glDeleteVertexArrays(1, &vaoSky);
    glDeleteBuffers(1, &vboBall);
    glDeleteTextures(1, &texEnvmap);
}

REGISTER_OBJECT(Node, Skybox)


